"""
# new Env("浓五的酒馆签到")
# cron: 15 11 * * *
✦ 小程序信息    ▸ 名称：浓五的酒馆
▸ 类别：积分抽奖
▸ 可换种类：抽奖得红包（50积分起抽奖）
✦ 执行说明    ▸ 首次执行需手动进入小程序完成手机号绑定
▸ 每日自动执行签到任务
"""
import os
import requests
import json
import time
from requests.exceptions import RequestException

# ========================== 第一部分：CK获取功能 ==========================

# 配置区
LOCAL_PROXY = "http://" + os.getenv("API_PROXY")  # 过检软件API地址
TARGET_APP_ID = "wxed3cf95a14b58a26"  # 目标小程序的APP_ID
LOGIN_URL_TEMPLATE = "https://stdcrm.dtmiller.com/std-weixin-mp-service/miniApp/custom/login"
CK_FILE = "浓五的酒馆.json"  # CK存储文件名

# 请求头
REQUEST_HEADERS = {
    'Host': 'stdcrm.dtmiller.com',
    'Connection': 'keep-alive',
    'Content-Length': '72',
    'authorization': '',
    'charset': 'utf-8',
    'User-Agent': 'Mozilla/5.0 (Linux; Android 10; V1824A Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.103 Mobile Safari/537.36 XWEB/1300473 MMWEBSDK/20230805 MMWEBID/5374 MicroMessenger/8.0.42.2460(0x28002A35) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android',
    'content-type': 'application/json',
    'Referer': 'https://servicewechat.com/wxed3cf95a14b58a26/226/page-frame.html',
    'Accept-Encoding': 'gzip,compress,br,deflate',
}

# 验证CK有效性的请求头
VALIDATE_HEADERS = {
    'Host': 'stdcrm.dtmiller.com',
    'Connection': 'keep-alive',
    'User-Agent': 'Mozilla/5.0 (Linux; Android 10; V1824A Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.103 Mobile Safari/537.36 XWEB/1300473 MMWEBSDK/20230805 MMWEBID/5374 MicroMessenger/8.0.42.2460(0x28002A35) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android',
    'content-type': 'application/json',
    'Referer': 'https://servicewechat.com/wxed3cf95a14b58a26/226/page-frame.html',
    'Accept-Encoding': 'gzip,compress,br,deflate',
}

def get_wx_list():
    """获取微信账户列表"""
    try:
        url = f"{LOCAL_PROXY}/getallwx"
        response = requests.get(url, timeout=10)
        return response.json() if response.status_code == 200 else []
    except Exception as e:
        print(f"获取微信列表失败: {str(e)}")
        return []

def get_wx_code(wxid):
    """获取小程序登录凭证"""
    try:
        url = f"{LOCAL_PROXY}/loginbyapp?Wxid={wxid}&appid={TARGET_APP_ID}"
        response = requests.get(url, timeout=10)
        if response.status_code == 200:
            return response.json().get("code")
        print(f"获取wxCode失败: HTTP {response.status_code}")
    except Exception as e:
        print(f"wxCode请求异常: {str(e)}")
    return None

def get_ck(wx_code):
    """获取小程序登录凭证（CK）"""
    payload = {
        "code": wx_code,
        "appId": TARGET_APP_ID
    }

    try:
        response = requests.post(
            LOGIN_URL_TEMPLATE, 
            headers=REQUEST_HEADERS, 
            json=payload, 
            timeout=15
        )
        print("响应体:", response.text)
        if response.status_code == 200:
            res_data = response.json()
            token = res_data.get('data')
            if token:
                return f"Bearer {token}"
            print("❌ 未能提取 token:", res_data)
        else:
            print(f"❌ 请求失败: HTTP {response.status_code}, {response.text}")
    except Exception as e:
        print(f"❌ 登录异常: {e}")
    
    return None

def validate_ck(ck):
    """验证 Bearer Token 的有效性"""
    url = "https://stdcrm.dtmiller.com/scrm-promotion-service/mini/wly/user/info"  
    headers = VALIDATE_HEADERS.copy()
    headers["authorization"] = ck  # 确保是 Bearer 开头

    try:
        response = requests.get(url, headers=headers, timeout=10)
        #print("响应:", response.text)
        if response.status_code == 200:
            data = response.json()
            if data.get("code") == 0:
                visitor = data.get("data", {}).get("visitor", {})
                print(f"✅ 认证成功，open_id: {visitor.get('open_id')}")
                return True
        return False
    except Exception as e:
        print("请求出错:", e)
        return False

def load_ck_from_file():
    """从文件加载CK并验证有效性，返回有效CK列表和失效账号列表"""
    valid_results = []
    expired_accounts = []
    
    if not os.path.exists(CK_FILE):
        return valid_results, expired_accounts, set()
    
    try:
        with open(CK_FILE, "r") as f:
            ck_results = json.load(f)
            # 验证CK有效性
            for acc in ck_results:
                if validate_ck(acc["ck"]):
                    valid_results.append(acc)
                else:
                    print(f"❌ CK已失效: {acc['wxname']}")
                    expired_accounts.append(acc)
        
        if valid_results:
            print(f"✅ 从文件加载 {len(valid_results)} 个有效CK")
        if expired_accounts:
            print(f"⚠️ 检测到 {len(expired_accounts)} 个失效CK")
            
        # 返回现有wxid集合用于检测新账号
        existing_wxids = {acc["wxid"] for acc in ck_results}
        return valid_results, expired_accounts, existing_wxids
    except Exception as e:
        print(f"加载CK文件失败: {str(e)}")
        return [], [], set()

def save_ck_to_file(ck_results):
    """保存CK到文件"""
    try:
        with open(CK_FILE, "w") as f:
            json.dump(ck_results, f, indent=2)
        print(f"💾 成功保存 {len(ck_results)} 个CK到 {CK_FILE}")
    except Exception as e:
        print(f"保存CK文件失败: {str(e)}")

def fetch_ck(wx_accounts):
    """
    获取指定微信账号的CK
    :param wx_accounts: 需要获取CK的微信账号列表
    :return: 新获取的CK列表
    """
    if not wx_accounts:
        print("❌ 未提供微信账户列表")
        return []
    
    print(f"✅ 准备获取 {len(wx_accounts)} 个微信账户的CK")
    
    ck_results = []
    for account in wx_accounts:
        wxid = account.get("Wxid")
        wxname = account.get("wxname", "未知账户")
        print(f"\n🔍 处理账户: {wxname}({wxid})")
        
        wx_code = get_wx_code(wxid)
        if not wx_code:
            print("❌ 获取wxCode失败")
            continue
        
        print(f"🔑 获取到wxCode: {wx_code[:8]}****")
        
        ck = get_ck(wx_code)
        if ck:
            print(f"🔐 获取CK成功: {ck[:23]}****")
            ck_results.append({
                "wxid": wxid,
                "wxname": wxname,
                "ck": ck
            })
        else:
            print("❌ 获取CK失败")
    
    if not ck_results:
        print("\n❌ 未获取到有效CK")
        return []
    
    return ck_results

def merge_ck_lists(original_list, new_list):
    """
    合并新旧CK列表
    :param original_list: 原始有效CK列表
    :param new_list: 新获取的CK列表
    :return: 合并后的CK列表
    """
    # 创建wxid到账号的映射
    account_map = {acc["wxid"]: acc for acc in original_list}
    
    # 更新或添加新CK
    for new_acc in new_list:
        account_map[new_acc["wxid"]] = new_acc
    
    return list(account_map.values())

def detect_new_accounts(current_wx_list, existing_wxids):
    """
    检测新的微信账号
    :param current_wx_list: 当前微信账号列表
    :param existing_wxids: 已有CK中的wxid集合
    :return: 新账号列表
    """
    new_accounts = []
    for account in current_wx_list:
        wxid = account.get("Wxid")
        if wxid not in existing_wxids:
            new_accounts.append(account)
    
    return new_accounts

# ========================== 第二部分：银鱼功能 ==========================

class EnvWrapper:
    """环境兼容类"""
    def __init__(self, name):
        self.name = name
    
    def isNode(self):
        """检查是否在Node环境中"""
        return True
    
    def wait(self, ms):
        """等待指定毫秒数"""
        time.sleep(ms / 1000.0)
    
    def get(self, url, headers):
        """发送GET请求"""
        try:
            response = requests.get(url, headers=headers, timeout=15)
            return response.text
        except Exception as e:
            print(f"GET请求失败: {str(e)}")
            return ""
    
    def post(self, url, headers, body):
        """发送POST请求"""
        try:
            response = requests.post(url, headers=headers, data=body, timeout=15)
            return response.text
        except Exception as e:
            print(f"POST请求失败: {str(e)}")
            return ""
    
    def done(self):
        """完成函数（占位）"""
        pass

def safe_json_parse(str_data):
    """安全解析JSON"""
    try:
        return json.loads(str_data)
    except:
        return None

def run_silverfish(ck_results):
    """执行银鱼功能"""
    if not ck_results:
        print("❌ 没有有效的CK可执行银鱼功能")
        return
    
    print("\n" + "="*50)
    print("🚀 开始执行")
    print("="*50)
    
    # 配置参数
    config = {
        "onlyWithdraw": False,   # true = 只提现, false = 先刷视频再提现
        "notify": False,          # 是否发送通知（在Python中暂不实现）
        "delay": 1500,           # 请求间隔时间(毫秒)
        "watchDuration": 80000,  # 模拟观看时长(毫秒)
        "baseVersion": "3.8.9"   # 更新为最新版本号
    }
    
    # 全局统计
    stats = {
        "totalAccounts": 0,
        "processedAccounts": 0,
        "successWithdraw": 0,
        "alreadyWithdraw": 0,
        "failedWithdraw": 0,
        "watchedVideos": 0
    }
    
    # 初始化环境
    env = EnvWrapper("🎬 银鱼质亨")
    
    # 构造账户列表
    accounts = [f"{acc['wxname']}#{acc['ck']}" for acc in ck_results]
    stats["totalAccounts"] = len(accounts)
    
    if not accounts:
        print("❌ 未找到有效的账号信息")
        return
    
    print(f"\n🎉 共找到 {len(accounts)} 个账号")
    
    for i, account in enumerate(accounts):
        account = account.strip()
        if not account: continue
        
        parts = account.split('#')
        remark = parts[0].strip() if len(parts) > 0 else ""
        auth = parts[1].strip() if len(parts) > 1 else ""
        account_name = remark or f"账号 {i + 1}"
        
        print(f"\n📌 ━━━━━━━━━━━━━ 开始处理 {account_name} ━━━━━━━━━━━━━")
        try:
            process_account(env, auth, account_name, config, stats)
            stats["processedAccounts"] += 1
        except Exception as e:
            print(f"❌ 处理账号 {account_name} 时出错: {str(e)}")
        
        # 账号间间隔
        if i < len(accounts) - 1:
            env.wait(2000)
    
    # 生成统计报告
    report = [
        '✅ 所有账号处理完成',
        '📊 统计报告:',
        f'├─ 总账号数: {stats["totalAccounts"]}',
        f'├─ 已处理账号: {stats["processedAccounts"]}',
        f'├─ 成功提现: {stats["successWithdraw"]}',
        f'├─ 今日已提现: {stats["alreadyWithdraw"]}',
        f'├─ 提现失败: {stats["failedWithdraw"]}',
        f'└─ 刷视频数: {stats["watchedVideos"]}'
    ]
    
    print("\n" + "\n".join(report))
    print("\n" + "="*50)
    print("✅ 银鱼执行完毕")
    print("="*50)

def process_account(env, auth, account_name, config, stats):
    """处理单个账号"""
    if config["onlyWithdraw"]:
        print('ℹ️ 只提现模式已启用，跳过刷视频步骤')
        do_withdraw(env, auth, account_name, stats)
    else:
        sign_actId = get_id(env, auth, account_name)
        if sign_actId:
            print(f'📽️准备签到...')
            watch_sign(env, sign_actId, auth, account_name, config, stats)
        print("结束签到")

def get_id(env, auth, account_name):
    """提取 title 为 '每日签到' 的 actId"""
    import json, re
    url = 'https://stdcrm.dtmiller.com/scrm-promotion-service/mini/module/config/list'
    headers = get_base_headers(auth)

    try:
        print(f'🔍 {account_name} 正在获取【每日签到】actId...')
        response = requests.post(url, headers=headers, json={})
        data = response.json()
        if not data or data.get("code") != 0:
            print(f'⚠️ {account_name} 请求失败: {data.get("msg", "未知错误")}')
            return None

        for module in data.get("data", []):
            for detail in module.get("detailList", []):
                try:
                    detail_json_str = detail.get("detailJson", "{}")
                    detail_json = json.loads(detail_json_str)

                    # ✅ title 在 detailJson 内
                    if detail_json.get("title") == "每日签到":
                        page_path = detail_json.get("jumpData", {}).get("pagePath", "")
                        #print(f"🎯 找到每日签到页面路径: {page_path}")
                        match = re.search(r"actId=([^&]+)", page_path)
                        if match:
                            act_id = match.group(1)
                            #print(f"✅ {account_name} 提取到 actId: {act_id}")
                            return act_id
                except Exception as parse_err:
                    print(f"⚠️ 解析 detailJson 出错: {parse_err} | 原始数据: {detail.get('detailJson')}")
        print(f"⚠️ {account_name} 未找到每日签到")
        return None
    except Exception as e:
        print(f'⚠️ {account_name} 获取 actId 异常: {str(e)}')
        return None

def watch_sign(env, sign_actId, auth, account_name, config, stats):
    """签到"""
    url = f'https://stdcrm.dtmiller.com/scrm-promotion-service/promotion/sign/today?promotionId={sign_actId}'
    headers = get_base_headers(auth)

    try:
        response = requests.get(url, headers=headers)  # ✅ 修复为 GET 请求
        #print(response.text)
        data = response.json()

        if data and data.get('code') == 0:
            print(f'✅ {account_name} 签到成功')
        else:
            msg = data.get('msg') or '未知错误'
            print(f'⚠️ {account_name} 签到失败: {msg}')
    except Exception as e:
        print(f'❌ {account_name} 签到请求异常: {str(e)}')

    # 若你确实需要等待
    if config.get("delay", 0) > 0:
        env.wait(config["delay"])

def get_base_headers(auth):
    """获取基础headers"""
    return {
        "Connection": "keep-alive",
        "Content-Length": "0",
        "charset": "utf-8",
        "Referer": "https://servicewechat.com/wxed3cf95a14b58a26/226/page-frame.html",
        "Host": "stdcrm.dtmiller.com",
        "authorization": auth,
        "User-Agent": "Mozilla/5.0 (Linux; Android 10; V1824A Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.103 Mobile Safari/537.36 XWEB/1300473 MMWEBSDK/20230805 MMWEBID/5374 MicroMessenger/8.0.42.2460(0x28002A35) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android",
        "content-type": "application/json",
        "Accept-Encoding": "gzip,compress,br,deflate"
    }

def get_withdraw_headers(auth):
    """获取提现headers"""
    return {
        "Accept": "application/json",
        "Accept-Language": "zh-CN,zh;q=0.9",
        "Accept-Encoding": "gzip, deflate, br",
        "Connection": "keep-alive",
        "Content-Type": "application/json",
        "Referer": "https://servicewechat.com/wx5b82dfe3747e533f/5/page-frame.html",
        "Host": "n05.sentezhenxuan.com",
        "Authori-zation": auth,
        "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.50(0x1800323d) NetType/WIFI Language/zh_CN",
        "Cb-lang": "zh-CN",
        "Form-type": "routine-zhixiang",
        "xweb_xhr": "1"
    }

# ========================== 主程序 ==========================

def main():
    print("="*50)
    print("浓五的酒馆签到")
    print("="*50)
    
    # 第一步：尝试加载已有的CK并验证有效性
    valid_ck_list, expired_accounts, existing_wxids = load_ck_from_file()
    
    # 第二步：获取当前微信账户列表
    print("\n🔄 正在获取当前微信账户列表...")
    current_wx_list = get_wx_list()
    
    if not current_wx_list:
        print("❌ 未获取到微信账户列表")
        return
    
    print(f"✅ 获取到 {len(current_wx_list)} 个微信账户")
    
    # 第三步：检测新账号
    new_accounts = detect_new_accounts(current_wx_list, existing_wxids)
    if new_accounts:
        print(f"🎉 检测到 {len(new_accounts)} 个新账号:")
        for acc in new_accounts:
            print(f"  - {acc.get('wxname', '未知账号')} ({acc.get('Wxid')})")
        
        # 获取新账号的CK
        new_ck_list = fetch_ck(new_accounts)
        if new_ck_list:
            # 合并新账号到有效列表
            merged_ck_list = merge_ck_lists(valid_ck_list, new_ck_list)
            valid_ck_list = merged_ck_list
            print(f"✅ 成功添加 {len(new_ck_list)} 个新账号的CK")
        else:
            print("❌ 未能获取新账号的CK")
    
    # 第四步：更新失效账号
    if expired_accounts:
        print("\n⚠️ 检测到失效CK，开始更新...")
        # 构建需要更新的账号列表（仅限当前存在的账号）
        update_accounts = []
        for expired in expired_accounts:
            # 查找当前微信列表中是否存在此wxid
            for wx_acc in current_wx_list:
                if wx_acc.get("Wxid") == expired["wxid"]:
                    update_accounts.append(wx_acc)
                    break
        
        if update_accounts:
            updated_ck_list = fetch_ck(update_accounts)
            if updated_ck_list:
                # 合并更新后的CK
                merged_ck_list = merge_ck_lists(valid_ck_list, updated_ck_list)
                valid_ck_list = merged_ck_list
                print(f"🔄 成功更新 {len(updated_ck_list)} 个失效账号的CK")
            else:
                print("❌ 未能更新失效账号的CK")
        else:
            print("⚠️ 失效账号已不在当前微信列表中，跳过更新")
    
    # 第五步：保存所有CK到文件
    if valid_ck_list:
        save_ck_to_file(valid_ck_list)
    else:
        # 如果没有有效CK，尝试获取所有账号的CK
        print("\n❌ 没有有效的CK，尝试获取所有账号的CK...")
        all_ck_list = fetch_ck(current_wx_list)
        if all_ck_list:
            save_ck_to_file(all_ck_list)
            valid_ck_list = all_ck_list
        else:
            print("❌ 未能获取任何CK，程序终止")
            return
    
    # 第六步：执行签到
    if valid_ck_list:
        run_silverfish(valid_ck_list)
    else:
        print("❌ 没有有效的CK可用，程序终止")

if __name__ == "__main__":
    main()